package com.stock.service;

import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import yahoofinance.Stock;
import yahoofinance.YahooFinance;
import yahoofinance.histquotes.HistoricalQuote;
import yahoofinance.histquotes.Interval;

import com.stock.db.StockDAO;
import com.stock.db.StockInfoDAO;
import com.stock.model.Point;
import com.stock.model.StockStatus;
import com.stock.model.StockVO;
import com.stock.util.EmailNotify;
import com.stock.util.Property;

public class StockService {
	private boolean isShowLog = false;
	
	private static final Logger log = LogManager.getLogger(StockService.class);
 
	public void showLog() {
		this.isShowLog = true;
	}
	
	public void computeTodayKeyPoing() throws Exception{
		log.info("Start computeTodayKeyPoing");
		StopWatch watch = new StopWatch();
		watch.start();
		StockInfoDAO stockInfoDAO = new StockInfoDAO();
		
		List stockList =  stockInfoDAO.querySymbol();
		 
		List<String> symbols =  new ArrayList<>();
		
		for (int i=0; i < stockList.size();i++) {
			Object[] obj =(Object[]) stockList.get(i);
			symbols.add(obj[0].toString());
			if (symbols.size() % 50==0) {
				String [] arr =  new String[symbols.size()];
				symbols.toArray(arr);
				loadStockTodayData(arr, true);
				symbols.clear();
			}
		}
		
		if (symbols.size() > 0) {
			String [] arr =  new String[symbols.size()];
			symbols.toArray(arr);
			loadStockTodayData(arr, true);
		}
		stockInfoDAO.close();
		watch.stop();
		log.info("End computeTodayKeyPoing exec time :" + watch.getTime() / 1000 + "ms" );
	}
	
    public void  loadStockHisttoryData(Calendar startDate, Calendar endDate, String symbol, boolean isInsertData) throws Exception{
         Stock yStock = null;
		 try {
			 yStock  = YahooFinance.get(symbol, startDate , endDate, Interval.DAILY);
			 if (yStock.getQuote() == null) {
				 throw new Exception("load stock errer(" + symbol +"]");
			 }
		 } catch (Exception e) {
			 e.printStackTrace();
			 throw e;
		 }
		 
		
		
		List<StockVO> stockList = new ArrayList<>();
		stockList.add(new StockVO(yStock.getQuote()));
		 
		 //找出最高價格與最低價格,與塞入歷史資料 
		 double yearHigh = 0;
		 double yearLow = Short.MAX_VALUE;
		 Date   dateHigh = null;
		 Date   dateLow = null;
		
		 for (HistoricalQuote hisStock : yStock.getHistory()) {
			 //無交易量　資料去除
			 if (hisStock.getVolume() == 0) continue;  
				 
			 if (hisStock.getHigh().doubleValue() > yearHigh) {
				 yearHigh = hisStock.getHigh().doubleValue() ; 
				 dateHigh = hisStock.getDate().getTime();
			 }
			 
			 if (hisStock.getLow().doubleValue()  < yearLow){
				 yearLow = hisStock.getLow().doubleValue() ;
				 dateLow = hisStock.getDate().getTime();
			 }
			 stockList.add(new StockVO(hisStock));
		 }
		 
		
		 
		 Collections.reverse(stockList);
		 
		 int startIndex = 0; //初始計算索引
		 
		 Point point = new Point();
		 point.setLowPoint1(yearLow);
		 
		 /*
		  * 計算一開始需計算是上升趨勢或下降趨勢 
		  */
		 for (StockVO stock : stockList) {
			
			 if (stock.getHigh() >= yearHigh) {
				 point.setStatusWard(StockStatus.UP_WARD);
				 break;
			 }
			 
			 if (stock.getLow() <= yearLow) {
				 point.setStatusWard(StockStatus.DOWN_WARD);
				 break;
			 }
			 startIndex++;
		 }
		 
		
		 
		 
		 if (isShowLog) {
			 log.info("股票代碼:" +  symbol);
			 log.info("初始起始日:" +  DateFormatUtils.ISO_DATE_FORMAT.format(stockList.get(startIndex).getTrade_date()));
			 log.info("初始趨勢:" + point.getStatusWard().getTagDesc());
			 log.info("最高價 :"+ DateFormatUtils.ISO_DATE_FORMAT.format(dateHigh) + " price :" + yearHigh);
			 log.info("最低價格 :" +DateFormatUtils.ISO_DATE_FORMAT.format(dateLow)  + " price :" + yearLow +"\n");
		 }
		 
		 stockList = stockList.subList(startIndex, stockList.size());
 
		 for (int i = 0; i < stockList.size(); i++) {
		 	 this.compuetStockKeyPoint(stockList.get(i), point, false);
		 } 
		 
		 
		 if (isShowLog) {
			 log.debug("日期 :"+DateFormatUtils.ISO_DATE_FORMAT.format(stockList.get(stockList.size()-1).getTrade_date())); 
			 log.debug("目前趨勢 :"+ point.getStatusWard().getTagDesc() );
			 log.debug("open :" + stockList.get(stockList.size()-1).getOpen());
			 log.debug("price :" + stockList.get(stockList.size()-1).getPrice());
			 log.debug("low :" + stockList.get(stockList.size()-1).getLow());
			 log.debug("high :" + stockList.get(stockList.size()-1).getHigh());
			 log.debug("lowPoint1 :"+point.getLowPoint1());
			 log.debug("lowPoint2 :"+point.getLowPoint2());
			 log.debug("highPoint1 :" + point.getHighPoint1());
			 log.debug("highPoint2 :"+ point.getHighPoint2());
			 log.debug("his HighPoint :" + point.getHisHighPoint());
			 
			 
			 DecimalFormat df = new DecimalFormat("###.00");
			 df.setMaximumFractionDigits(2);
			 
			 if (point.getLowPoint1() < point.getLowPoint2() || point.getLowPoint2() ==0) {
				 log.debug("支持點:" +  df.format((point.getLowPoint1() -(point.getLowPoint1() * Property.KEY_PRICE))) + " ~ " +  df.format((point.getLowPoint1()+(point.getLowPoint1() * Property.KEY_PRICE))));
			 } else {
				 log.debug("支持點:" +  df.format((point.getLowPoint2() -(point.getLowPoint2() * Property.KEY_PRICE))) + " ~ " +  df.format((point.getLowPoint2()+(point.getLowPoint2() * Property.KEY_PRICE))));
			 }
			 
			 if (point.getHighPoint1() > point.getHighPoint2()) {
				 log.debug("壓力點:" +  df.format((point.getHighPoint1() -(point.getHighPoint1() * Property.KEY_PRICE))) + " ~ " +  df.format((point.getHighPoint1()+(point.getHighPoint1() * Property.KEY_PRICE))));
			 } else {
				 log.debug("壓力點:" +  df.format((point.getHighPoint1() -(point.getHighPoint1() * Property.KEY_PRICE))) + " ~ " +  df.format((point.getHighPoint1()+(point.getHighPoint1() * Property.KEY_PRICE))));
			 }
		 }
		 if (isInsertData) {
			 StockDAO dao = new StockDAO();
			 dao.batchInsertStackVO(stockList);
			 dao.close();
		 }
	}
	
    public void  loadStockTodayData(String[] symbols, boolean isSendNotify) throws Exception {
	   	 log.debug("Start load today stock data");
	   	 
		 Map<String, Stock> stocks = YahooFinance.get(symbols);
		 
		 log.info("query params symbos :" + StringUtils.join(symbols, ","));
		 log.info("get stock size:" + stocks.size());
		  
	   	 StockDAO dao = new StockDAO();
	   	 
	   	 
	   	 for (Entry <String, Stock>  entry : stocks.entrySet()) {
	   		 String symbol = entry.getKey();
	   		 Stock  yStock = entry.getValue();
	   		
	   		 //前今日個股
			 StockVO stock = new StockVO(yStock.getQuote());
	   		 /*
			 if (stock.getVolume() ==0) {
				 log.warn(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " ["+ symbol + "] 無交易量!!");
				 continue;
			 }*/
			 
			 
			 //前交易日個股
			 StockVO stockUp = dao.getStockUp(symbol, DateFormatUtils.format(stock.getTrade_date(), "yyyy/MM/dd"));
			 
			 if (stockUp == null) {
				 log.error( DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " ["+ symbol + "] T-1 資料錯誤!!!");
				 continue;
			 }
			 
			
			 /*
			 //日期都相同　資料已經存入資料庫
			 if (StringUtils.equals(
				 DateFormatUtils.ISO_DATE_FORMAT.format(stockUp.getTrade_date()),
				 DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()))) {
				 log.warn(DateFormatUtils.ISO_DATE_FORMAT.format(stockUp.getTrade_date()) + " ["+ symbol + "] 已經存在資料庫!!");
				 continue;
			 }*/
			 
			 Point point = new Point();
			 point.setStatusWard(stockUp.getStockStatus());
			 point.setLowPoint1(stockUp.getLowPoint1());
			 point.setLowPoint2(stockUp.getLowPoint2());
			 point.setHighPoint1(stockUp.getHighPoint1());
			 point.setHighPoint2(stockUp.getHighPoint2());  
			 point.setHisHighPoint (stockUp.getHisHighPoint());
			 point.setCheckPoint(Boolean.valueOf(stockUp.getCheckPoint()));
			 
			 
			 
			 this.compuetStockKeyPoint(stock, point, isSendNotify);
   	   
			 log.info(MessageFormat.format(
					 "tradeDate:{0}, symbol:{15}, isPoint:[{1}], signal:{2}, status :[{3}]==>[{4}], price:{5}, high:{6}, low:{7}, open:{8},"
					 +" L1:{9}, L2:{10}, H1:{11}, H2:{12}, hisH:{13}, note:{14}", 
					 DateFormatUtils.format(stock.getTrade_date(), "yyyy/MM/dd"),
					 Boolean.valueOf(stock.getCheckPoint()) ? "是" : "否",
					 stock.getSignalStatus(),
					 stockUp.getStockStatus().getTagDesc(),
				     stock.getStockStatus().getTagDesc(),
				     stock.getPrice(),
				     stock.getHigh(),
				     stock.getLow(),
				     stock.getOpen(),
				     stock.getLowPoint1(),
				     stock.getLowPoint2(),
				     stock.getHighPoint1(),
				     stock.getHighPoint2(),
				     stock.getHisHighPoint(),
				     ObjectUtils.defaultIfNull(stock.getNote(), ""),
				     stock.getSymbol()
			 ));
			 
			 dao.saveOrUpdateStockVO(stock);
	   	 }
	   	 dao.close();
	   	 log.debug("End load today stock data");
	}
    
    @SuppressWarnings("unused")
	private void compuetStockKeyPoint(StockVO stock , Point point ,  boolean isNotify) {
		double high = stock.getHigh();
		double price = stock.getPrice();
		double low = stock.getLow();
		StockStatus statusWard = point.getStatusWard();
		double highPoint1  = point.getHighPoint1();
		double highPoint2 = point.getHighPoint2();
		double lowPoint1 = point.getLowPoint1();
		double lowPoint2 =point.getLowPoint2();
		double hisHighPoint= point.getHisHighPoint();
		boolean isCheckPoint = point.isCheckPoint();
		
		Date tradeDate = stock.getTrade_date();
		 
		if(debug("2014/11/03", tradeDate)) {
			 int n=1;
		}
    	
		switch(statusWard.getTagIndex()){
			case 1://下降趨勢自然反彈
				 
				
				//紀錄 第一次最高價格 關鍵點
				if (high > highPoint2) { 
					highPoint2 = high;
				} 
				
				if (price < lowPoint1 - (lowPoint1 * Property.KEY_PRICE) ) { 
					/*價格低於 第一關鍵低點 +3  轉下降趨勢 */ 
					lowPoint1 = low;
					highPoint1 = highPoint2;
					
					
					stock.setStockStatus(StockStatus.DOWN_WARD);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
					statusWard = StockStatus.DOWN_WARD;
					
					lowPoint1 = low;
					lowPoint2 =0;
					hisHighPoint =  highPoint1 > highPoint2 ? highPoint1 : highPoint1;
					highPoint1 = 0; //取代前高
					highPoint2 =0;
				} else if (price <= highPoint2 - (highPoint1 * Property.UP_DOWN_PRICE)) {
					/* 價格小於第一次高價關鍵點  振福6元 
					 * 轉自然回檔 
					 */
					lowPoint2 = low;
					stock.setStockStatus(StockStatus.DOWN_NATURAL_REACTION);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
					statusWard = StockStatus.DOWN_NATURAL_REACTION;
				} 
				 
				if (price >= highPoint1 - (highPoint1 * Property.KEY_PRICE)) {
					isCheckPoint = false;
					stock.setStockStatus(StockStatus.SECONDARY_RALLY);
					stock.setCheckPoint(String.valueOf(isCheckPoint));
					
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
					statusWard = StockStatus.SECONDARY_RALLY;
				} 
				
				break;
			case 2: //下降趨勢自然回檔
				//記錄第二關鍵點 最低價格
				flag : {
					if (low < lowPoint2 ) {
						lowPoint2 = low;
					}
					
					if (price < lowPoint1 -  (lowPoint1 * Property.KEY_PRICE)) {
						/*價格低於 第一關鍵低點 +3  轉下降趨勢 */ 
						stock.setStockStatus(StockStatus.DOWN_WARD);
						statusWard = StockStatus.DOWN_WARD;
						
						
						lowPoint1 = low;
						lowPoint2 =0;
						hisHighPoint =  highPoint1 > highPoint2 ? highPoint1 : highPoint1;
						highPoint1 = highPoint1 > highPoint2 ? highPoint2 : highPoint1; //取代前高
						highPoint2 =0;
					} else if (price > lowPoint1 - (lowPoint1 * Property.KEY_PRICE) && price < lowPoint1 + (lowPoint1 * Property.KEY_PRICE)) {
						//介於L1 +-3 進入次級反彈 關鍵點觀察
						isCheckPoint = false;
						stock.setStockStatus(StockStatus.SECONDARY_RALLY);
						stock.setCheckPoint(String.valueOf(isCheckPoint));
						statusWard = StockStatus.SECONDARY_RALLY;
					} else  if (price >= lowPoint2 + (lowPoint2 * Property.KEY_PRICE)) {
						isCheckPoint = false;
						stock.setStockStatus(StockStatus.SECONDARY_RALLY);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
						statusWard = StockStatus.SECONDARY_RALLY;
						
						break flag; //進入次級反彈
					} 			
					break;
				}
			case 3: //次級反彈
				if (lowPoint2 ==0) {
					lowPoint2 = low;
				} if (low < lowPoint2 ) {
					lowPoint2 = low;
				}
				
				if (high > highPoint2 ) {
					highPoint2 = high;
				}
				
				//轉下降趨勢
				if (price <= lowPoint1 -  (lowPoint1 * Property.KEY_PRICE)) {
					isCheckPoint = false;
					
					stock.setStockStatus(StockStatus.DOWN_WARD);
					stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [次級反彈] L1 關鍵停損點 :" + price);
					stock.setSignalStatus(Property.SIGAL_SELL);
					
					 
					
					//如果有持股 這邊是停損點,之後需要發出訊號
					keyPointMessage(stock.getNote());
					sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					statusWard = StockStatus.DOWN_WARD;
					
					lowPoint1 = low;
					lowPoint2 =0;
					hisHighPoint =  highPoint1 > highPoint2 ? highPoint1 : highPoint1;
					highPoint1 = highPoint1 > highPoint2 ? highPoint2 : highPoint1; //取代前高
					highPoint2 =0;
				} else if (!isCheckPoint){
					/*
					 * 進入關鍵點觀察點 
					 * L1關鍵點比L2關鍵點高  then price > L1 +3
					 * L1關鍵點比L2關鍵點高 then price > L2 +3
					 */
					if (lowPoint1 < lowPoint2 && price >= lowPoint1 + (lowPoint1 * Property.KEY_PRICE)) {
						/**  發出買進訊號 */
						isCheckPoint = true;
						stock.setSignalStatus(Property.SIGAL_BUY);
						stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [次級反彈] 關鍵買進點 :" + price);
						stock.setCheckPoint(String.valueOf(isCheckPoint));
						stock.setStockStatus(statusWard);
						
						
						keyPointMessage(stock.getNote());
						sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
					} else if (lowPoint1 > lowPoint2 && price >= lowPoint2 + ( lowPoint2 * Property.KEY_PRICE)) {
						/**  發出買進訊號 */
						isCheckPoint = true;
						stock.setSignalStatus(Property.SIGAL_BUY);
						stock.setCheckPoint(String.valueOf(isCheckPoint));
						stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [次級反彈] 關鍵買進點 :" + price);
						stock.setStockStatus(statusWard);
						
						
						keyPointMessage(stock.getNote());
						sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
					}	
				} else if (isCheckPoint) { //如果進入關鍵點 ，則觀察價格是否有能力突破上檔關鍵H1
					if (high > highPoint2 ) {
						highPoint2 = high;
					}
					
					//如果到達超過上一波高點 轉   上升趨勢自然反彈
					if (price >= highPoint1) {
						isCheckPoint = false;
						
						stock.setStockStatus(StockStatus.UP_NATURAL_RALLY);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
						statusWard = StockStatus.UP_NATURAL_RALLY;
					}
					
				}					 						 						
				break;
			case 4: //上升趨勢自然反彈 					
				 
				if (high > highPoint2 ) {
					highPoint2 = high;
				}
				
				if (low < lowPoint2 ) {
					lowPoint2 = low;
				}
				
				
				  
				//突破關鍵轉為上升趨勢,發出訊號 可以在此加碼
				/** 發出加碼 訊號 */
				if (price >= highPoint1 + ( highPoint1 * Property.KEY_PRICE)){
					//記錄該波段 低點
					if (lowPoint1 > lowPoint2 ) {
						lowPoint1 = lowPoint2;
					}
					isCheckPoint = false;
					
					
					stock.setStockStatus(StockStatus.UP_WARD);
					stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [上升趨勢自然反彈] 加碼買進點 :" + price);
					stock.setSignalStatus(Property.SIGAL_ADD_BUY);
					
					//發出加碼買進訊號
					keyPointMessage(stock.getNote());
					sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
					
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					statusWard = StockStatus.UP_WARD;
					
					lowPoint1 = lowPoint1 < lowPoint2?  lowPoint2 : lowPoint1;
					lowPoint2 =0;
					hisHighPoint = highPoint1;// 記錄前一高點
					highPoint1 =highPoint2; //取代前一個關鍵高點
					highPoint2 =0;
					break;
				}
				
				 
				if (!isCheckPoint) {
					if (price >= highPoint1 ) {
						isCheckPoint = true;
					} else if (price >= lowPoint2 + (lowPoint2 * Property.KEY_PRICE) ) {
						isCheckPoint = true;
					}
				} else if (isCheckPoint) {
					//轉 次級次級回檔
					/**   發出賣出 訊號 */
					if(price < highPoint1 - (highPoint1 * Property.KEY_PRICE) ||   price < lowPoint2) {
						isCheckPoint = false;
						
						stock.setStockStatus(StockStatus.SECONDARY_REACTION);
						stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [上升趨勢自然反彈] H1 關鍵賣出點 :" + price );
						stock.setSignalStatus(Property.SIGAL_SELL);
						
						
						keyPointMessage(stock.getNote());
						sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint);  
						statusWard = StockStatus.SECONDARY_REACTION;
						lowPoint2 =low;
					}  
				} else {
					if(price <= lowPoint1 - (lowPoint1 * Property.KEY_PRICE)  ) {
						
						stock.setStockStatus(StockStatus.DOWN_WARD);
						stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [上升趨勢自然反彈] L1 關鍵賣出點 :" + price );
						
						keyPointMessage(stock.getNote());
						sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
						statusWard = StockStatus.DOWN_WARD;
						
						lowPoint1 = low;
						lowPoint2 =0;
						hisHighPoint =  highPoint1 > highPoint2 ? highPoint1 : highPoint1;
						highPoint1 = highPoint1 > highPoint2 ? highPoint2 : highPoint1; //取代前高
						highPoint2 =0;
					}
				}
				break;
			case 5: //上升趨勢 
				if (high > highPoint1 ) {
					highPoint1 = high;
				}
			    
				if (lowPoint2 == 0) {
					lowPoint2 =low;
				}
				 
				if (price <= highPoint1 - (highPoint1 * Property.KEY_PRICE)) {
					if (low < lowPoint2) {
						lowPoint2 = low;
					} 
				} else {
					if (low > lowPoint2) {
						lowPoint2 = low;
					} 
				}
				
				 /*
				  * 轉 上升趨勢自然回檔 
				  */
				if (price <= highPoint1 - (highPoint1 * Property.UP_DOWN_PRICE)) {
					stock.setStockStatus(StockStatus.UP_NATURAL_REACTION);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					statusWard = StockStatus.UP_NATURAL_REACTION;
					
					lowPoint1 = low;
					lowPoint2 = 0;					
					highPoint2 =0;
					break;
				}
				
				//上升趨勢 若小於上一波 高點 ，則發出賣出訊號
				if (price <= hisHighPoint - (hisHighPoint * Property.KEY_PRICE) ||
					price <= lowPoint2 -(lowPoint2 * Property.UP_DOWN_PRICE)) {
					stock.setSignalStatus(Property.SIGAL_SELL);
					stock.setNote(DateFormatUtils.ISO_DATE_FORMAT.format(stock.getTrade_date()) + " [上升趨勢] HIS H1 關鍵賣出點 :" + price);
					//tock.setStockStatus(StockStatus.UP_WARD);
				
					//寄發通知 賣出訊號
					keyPointMessage(stock.getNote());
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					sendKyeMessage(isNotify, stock, statusWard, lowPoint1, lowPoint2, highPoint1, highPoint2, hisHighPoint);
				}  
				break;
			case 6: //上升趨勢自然回檔
				 if (low < lowPoint1) {
					lowPoint1 = low;
				 }
				
				 //轉上升趨勢自然反彈
				 if (price >= lowPoint1 + (lowPoint1 * Property.UP_DOWN_PRICE)) {
					
					stock.setStockStatus(StockStatus.UP_NATURAL_RALLY);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					statusWard = StockStatus.UP_NATURAL_RALLY;
					
					lowPoint2 = 0;					
					highPoint2 =0;
				 }
				 
				 break;
			case 7: //次級回檔
				//關鍵低檔 L1-3 轉 下降趨勢
				if (low < lowPoint2 ) {
					lowPoint2 = low;
				}
				
				if (high > highPoint2 ) {
					highPoint2 = high;
				}
				
				 
				//轉下降趨勢 
				if(price <= lowPoint1) {
					isCheckPoint = false;
					stock.setStockStatus(StockStatus.DOWN_WARD);
					debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					statusWard = StockStatus.DOWN_WARD;
					
					lowPoint1 = low;
					lowPoint2 =0;
					hisHighPoint =  highPoint1 > highPoint2 ? highPoint1 : highPoint1;
					highPoint1 = highPoint1 > highPoint2 ? highPoint2 : highPoint1; //取代前高
					highPoint2 =0;
					break;
				}
				 
				if (isCheckPoint) {
					if (lowPoint1 < lowPoint2  &&  price >= lowPoint1 + (lowPoint1 * Property.KEY_PRICE)) {
						isCheckPoint = false;
						stock.setStockStatus(StockStatus.SECONDARY_RALLY);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
						statusWard = StockStatus.SECONDARY_RALLY;
					}  else if (lowPoint1 > lowPoint2  &&  price >= lowPoint2 + (lowPoint2 * Property.KEY_PRICE)) {
						isCheckPoint = false;
						stock.setStockStatus(StockStatus.SECONDARY_RALLY);
						debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
						statusWard = StockStatus.SECONDARY_RALLY;
					}  
					
				} else {
					if (price >= lowPoint1 - (lowPoint1 * Property.KEY_PRICE) &&  price <= lowPoint1 + (lowPoint1 * Property.KEY_PRICE)  ) {
						isCheckPoint = true;
					}
					
					if (price >= lowPoint2 + (lowPoint2 * Property.KEY_PRICE)) {
						isCheckPoint = true;
					} 
				}
				break;					
			case 8: //下降趨勢
			 
				isCheckPoint = false;
				
				if (low < lowPoint1 ) {
					lowPoint1 = low;
				}
				
				if (high > highPoint1 ) {
					highPoint1 = high;
				}
				 
				//最低價格 加上振福  超過則紀錄為自然反彈
				if (price >= lowPoint1 + (lowPoint1 * Property.UP_DOWN_PRICE)) {
					
					 stock.setStockStatus(StockStatus.DOWN_NATURAL_RALLY);
					 //highPoint1= stock.getHigh();
					 debugLog(stock, statusWard, lowPoint1, lowPoint2 ,  highPoint1,  highPoint2, hisHighPoint); 
					 statusWard = StockStatus.DOWN_NATURAL_RALLY;
					 highPoint2=0;
				} 
				break;
		 }
    	
    	 stock.setLowPoint1(lowPoint1);
		 stock.setLowPoint2(lowPoint2);
		 stock.setHighPoint1(highPoint1);
		 stock.setHighPoint2(highPoint2);
		 stock.setHisHighPoint(hisHighPoint);
		 stock.setCheckPoint(String.valueOf(isCheckPoint));
		 stock.setStockStatus(statusWard);
		 
		 point.setLowPoint1(lowPoint1);
		 point.setLowPoint2(lowPoint2);
		 point.setHighPoint1(highPoint1);
		 point.setHighPoint2(highPoint2);
		 point.setHisHighPoint(hisHighPoint);
		 point.setCheckPoint(isCheckPoint);
		 point.setStatusWard(statusWard);
    }
    
	
	
    private boolean debug(String debugDate, Date tradeDate) {
    	//debugDate = StringUtils.replace(debugDate, "-", "/");
		return debugDate.equals(DateFormatUtils.format(tradeDate, "yyyy/MM/dd"));
	}
	
	private void keyPointMessage(String msg) {
		if (!isShowLog) return;
		System.err.println(msg);
	}
	
	public void sendKyeMessage(boolean isNotify ,StockVO stock, StockStatus stockStatus, double lowPoint1, double lowPoint2 , double highPoint1, double highPoint2, double hisHighPoint) {
		if (!isNotify) return;
		
		log.info("send email ["+stock.getSymbol()+"] start");
		DecimalFormat df = new DecimalFormat(" 00.00");
		df.setMaximumFractionDigits(2);
		String subject = MessageFormat.format("[{0}] {2}",
		   stock.getSymbol(),
		   DateFormatUtils.format(stock.getTrade_date(), "yyyy/MM/dd"),
		   stock.getNote()
		); 
		
		String text = MessageFormat.format(
				"{19}\n{0} [{1}]-->[{2}] \nopen:{3}, \nhigh:{4}, \nlow:{5} \nprice :{6} \nhisHighPoint:{20}" 
				+ "\nlowPoint1  :{7}\trange:{8} ~ {9}"
				+ "\nlowPoint2  :{10}\trange:{11} ~ {12}"
				+ "\nhighPoint1 :{13}\trange:{14} ~ {15}"
				+ "\nhighPoint2 :{16}\trange:{17} ~ {18}", 
				 DateFormatUtils.format(stock.getTrade_date(), "yyyy-MM-dd"),
				 stockStatus.getTagDesc(),
				 stock.getStockStatus().getTagDesc(),
				 stock.getOpen(),
				 stock.getHigh(),
				 stock.getLow(),
				 stock.getPrice(),
				 df.format(lowPoint1),   df.format(lowPoint1 - (lowPoint1 * Property.KEY_PRICE)) , df.format(lowPoint1 + (lowPoint1 * Property.KEY_PRICE)),
				 df.format(lowPoint2),   df.format(lowPoint2 - (lowPoint2 * Property.KEY_PRICE)) , df.format( lowPoint2 + (lowPoint2 * Property.KEY_PRICE)),
				 df.format(highPoint1),  df.format(highPoint1 - (highPoint1 * Property.KEY_PRICE)) , df.format(highPoint1 + (highPoint1 * Property.KEY_PRICE)),
				 df.format( highPoint2) , df.format(highPoint2 - (highPoint2 * Property.KEY_PRICE)) , df.format(highPoint2 + (highPoint2 * Property.KEY_PRICE)),
				 stock.getNote(),
				 hisHighPoint
				
		 );
		 EmailNotify.send(Property.EMAIL_TO, subject, text);
	}
	
	private void debugLog(StockVO stock, StockStatus stockStatus, double lowPoint1, double lowPoint2 , double highPoint1, double highPoint2, double hisHighPoint) {
		if (!isShowLog) return;
		
		DecimalFormat df = new DecimalFormat(" 00.00");
		 
		df.setMaximumFractionDigits(2);
		 
		System.out.println(MessageFormat.format("{0} [{1}]-->[{2}] \nopen:{3}, high:{4}, low:{5} price :{6} \nhisHighPoint:{19}"
				+ "\nlowPoint1  :{7}\trange:{8} ~ {9}"
				+ "\nlowPoint2  :{10}\trange:{11} ~ {12}"
				+ "\nhighPoint1 :{13}\trange:{14} ~ {15}"
				+ "\nhighPoint2 :{16}\trange:{17} ~ {18}", 
				 DateFormatUtils.format(stock.getTrade_date(), "yyyy/MM/dd"),
				 stockStatus.getTagDesc(),
				 stock.getStockStatus().getTagDesc(),
				 stock.getOpen(),
				 stock.getHigh(),
				 stock.getLow(),
				 stock.getPrice(),
				 df.format(lowPoint1),   df.format(lowPoint1 - (lowPoint1 * Property.KEY_PRICE)) , df.format(lowPoint1 + (lowPoint1 * Property.KEY_PRICE)),
				 df.format(lowPoint2),   df.format(lowPoint2 - (lowPoint2 * Property.KEY_PRICE)) , df.format( lowPoint2 + (lowPoint2 * Property.KEY_PRICE)),
				 df.format(highPoint1),  df.format(highPoint1 - (highPoint1 * Property.KEY_PRICE)) , df.format(highPoint1 + (highPoint1 * Property.KEY_PRICE)),
				 df.format( highPoint2) , df.format(highPoint2 - (highPoint2 * Property.KEY_PRICE)) , df.format(highPoint2 + (highPoint2 * Property.KEY_PRICE)),
				 hisHighPoint
				
		 ));
		 System.out.println("===================================================>>\n");
	}
}
